from pathlib import PurePath
from typing import Literal

from fastapi import Request, HTTPException, Depends, Header
from fastapi.security import HTTPBasic, HTTPBasicCredentials

from .config import app_setting
from .resource_model import (
    dst_path_to_local, local_model_adapter, get_os_stat, convert_to_local_path,
    get_operate_layer, pan115_model_adapter, ResourceModel, ResourceCreator, Pan115ResourceCreator,
    LocalResourceCreator
)
from .xml_utils import PropXMLParser

security = HTTPBasic(realm="WebDav Auth")

dst_path_to_local = dst_path_to_local


def auth(credentials: HTTPBasicCredentials = Depends(security)):
    user_name, passwd = credentials.username, credentials.password
    s = f"{user_name}:{passwd}"
    if s not in app_setting.USER_PASSWD:
        raise HTTPException(
            status_code=401,
            headers={"WWW-Authenticate": 'Basic realm="WebDav Auth"'},
        )


async def propfind_info(request: Request, depth: str = Header(default="infinity")):
    raw_body = await request.body()
    try:
        parser = PropXMLParser(raw_body)
        return *parser.find_query_props(), depth
    except Exception:
        raise HTTPException(400)


def is_sub_path(p1: PurePath, p2: PurePath) -> bool:
    """
    判断p2是否为p1的子路径
    :param p1:
    :param p2:
    :return:
    """
    try:
        _ = p2.relative_to(p1)
        return True
    except ValueError:
        return False


def _path_router_helper(req_path: PurePath) -> Literal["local", "pan115"]:
    if app_setting.LOCAL_ROOT_PATH and is_sub_path(app_setting.LOCAL_MOUNT_PATH, req_path):
        return 'local'

    if app_setting.PAN_115_MOUNT_PATH and is_sub_path(app_setting.PAN_115_MOUNT_PATH, req_path):
        return 'pan115'

    raise HTTPException(404)


async def resource_model_router(
        request: Request, op_layer=Depends(get_operate_layer),
        depth: str = Header(default="infinity")
) -> ResourceModel:
    req_path = PurePath(request.scope['path'])
    drive = _path_router_helper(req_path)
    if drive == 'local':
        local_path = convert_to_local_path(req_path)
        stat_info = await get_os_stat(local_path)
        model = local_model_adapter(stat_info)
        return model

    if drive == 'pan115':
        if request.method.upper() == "PROPFIND":
            depth_ = 0 if depth == "0" else 1
        else:
            depth_ = int(request.query_params.get("depth", 0))
        # noinspection PyTypeChecker
        model = await pan115_model_adapter(req_path, depth_, op_layer)
        return model

    raise HTTPException(404)


async def resource_creator_router(
        request: Request, op_layer=Depends(get_operate_layer)
) -> ResourceCreator:
    """
    根据请求路径返回一个ResourceCreator对象
    :param request:
    :param op_layer:
    :return:
    """
    req_path = PurePath(request.scope['path'])
    drive = _path_router_helper(req_path)
    if drive == 'local':
        local_path = convert_to_local_path(req_path)
        creator = LocalResourceCreator(local_path)
        return creator

    if drive == 'pan115':
        creator = Pan115ResourceCreator(req_path, op_layer)
        return creator
